Character-Oriented Device Drivers Robert A. Gibson Raleigh Personal Computer Club In the September issue of the Exchange from IBM, there were two articles concerning the ANSI.SYS character-oriented device driver which comes with versions 2.0 and 2.1 of DOS. This article is a general discussion of device drivers, which uses ANSI.SYS as an example. First, refer to Chapters 13 and 14 of the DOS 2.0 manual for more detail about device drivers, particularly the sections "Using Extended Screen and Keyboard Control" and "Installable Device Drivers". How does a device driver function? In order to understand let's first look at how one might be used. Application Program Input/Output Request ---------------------------------------- 1 PROGRAM test(Input,Output); 3 VAR 4 outfile : Text; 6 BEGIN 7 Assign(outfile,"CON:"); 8 Rewrite(outfile); 9 Writeln(outfile,"Testing 1 2 3"); 10 END. ------------------------------------- Figure 1. Sample Pascal Program In this sample Pascal program, line 7 associates the program file "outfile" with an operating system device called "CON:" (i.e., the console). In DOS, as in most operating systems, the console represents the keyboard and the display. When the program outputs to the console (see line 9), the character sequence is supposed to be displayed on the screen, one character at a time. If we have a character device driver defined for the console, then it would receive the characters one at a time and, in turn, display them on the console. What, then, is the purpose of the console device driver? With the device driver present, the application program doesn't have to be aware of the specifics about the output device. It could be something simple, as a typewriter, or something exotic, as a holographic image device. As far as the program is concerned, all it has to do is request that DOS output characters. DOS, in turn, requests that the driver output the characters, one at a time. In addition to this simple interfacing task (the driver acts as an interface between the physical device and the operating system), a device driver can act as intelligent device controller. It does this by scanning outgoing characters for special control sequences. The "special sequences" recognized by the standard DOS 2.0 console device driver (and which originated with the ANSI X3.64-1979 standards committee) are defined in chapter 13 of the DOS manual. Since the console device driver acts as both an input and output interface, there is a buffering of characters in each direction. On input, if a user presses keys before input has been requested, some of the characters (around 15) are held in the input buffer until a program, or DOS, requests input. On the output side, if a program is displaying information, the data could contain one of the special control sequences, and the driver has to hold the characters until it can decide whether the sequence is, or is not, a control sequence. We can diagram this relationship as follows: Physical Devices Console Device Driver |---------------| |--------| | |-----------| | |------| Display <-- Output Buff <-- |--------| |-----------| Appli- cation |--------| |-----------| Program Keyboard --> Input Buff --> |--------| | |-----------| | |------| |---------------| Figure 2 Relationship between Console Device Driver and Physical Devices Now that we know (conceptually) how a character device driver works, how do we design and implement one? Very carefully! How come? Consider the fact that when we power on (or reboot) the PC, it loads the operating system. This, in turn, must load and initialize all of the device drivers BEFORE it begins to look for the AUTOEXEC.BAT file. This means that if we are rewriting the console device driver, it has to function well enough to perform the systems output to the screen and input from the keyboard before we can even interactively debug it. If it doesn't, we have to power off and reboot with a backup copy of the operating system that doesn't contain our changed console device driver. In order to have a functioning device driver, we need to know the kinds of requests that are going to be made of the driver by the operating system. If we look in chapter 14 of the DOS 2.0 manual, we find that the list of possible commands to be performed are: ------------------------------------ 0 - Initialization 1 - Media Check 2 - Build BPB 3 - IOCTL Input 4 - Input 5 - Non-Destructive Input (no wait) 6 - Input Status 7 - Input Flush 8 - Output 9 - Output (with verify) 10 - Output Status 11 - Output Flush 12 - IOCTL Output ------------------------------------ Fig 3. Device Driver Command Requests I'm not going into a detailed study of each of the possible device commands. What I will do is give you an idea about what goes on in a character device driver for the console. If you were to think of the device driver as written in a high level language such as Pascal, then the main portion of the program could be thought of as something like the following CASE statement, where each command request is satisfied by a separate procedure. ------------------------------------ CASE command OF 0 : Init; 1 : Media_Check; 2 : Build_BPB; 3 : IOCTL_Input; 4 : Input_Request; 5 : Non_Destructive_Input; 6 : Input_Status; 7 : Input_Flush; 8 : Output_Request; 9 : Output_with_verify; 10 : Output_Status; 11 : Output_Flush; 12 : IOCTL_Output; END; ------------------------------------ Figure 4. Case Statement At boot time, DOS is loaded and looks for a file named "CONFIG.SYS" on the default drive. This file defines the configuration information to be used by the operating system. Specifically, there will be a reference for each of the device drivers to be loaded. In our case, one of the records will look something like "DEVICE=ANSI.SYS". This entry tells DOS to look for a file named "ANSI.SYS" on the default drive and directory and to load it as a device driver. After it is loaded, the driver is invoked with an initialization request (command 0). The driver performs whatever initialization processing is required, and it returns to DOS with an indication of how much storage is required. In the case of the console device driver, the operating system then invokes the driver whenever a keyboard input request or display output request is initiated. In order to perform keyboard input, DOS invokes the console device driver to determine if the user has pressed a key. To do this, DOS will use either the "Input" or "Non-destructive Input (no wait)" device command. The driver will check the input buffer and return the appropriate information to DOS. In order to display text on the console, DOS will use either the "Output" or "Output (with verify)" command. As the characters are received by the driver, they are checked to see if they conform to any of the special control sequences. If they do not, they are placed on the display. If case you haven't noticed, the console device driver that comes with DOS 2.0 takes sequences that it thinks may be special control sequences, but which it eventually decides that it doesn't recognize. What are these "special control sequences"? If you look in chapter 13 of the DOS 2.0 manual, you'll see what I mean. Each of the special output sequences begins with an escape character (CHR(27)), therein referred to as ESC, followed immediately by an open left bracket (CHR(91)). If you're curious about how this particular character combination was chosen, take a look at the ANSI X3.64-1979 standards document (which in turn references ANSI X3.4-1977, which defines the ASCII character set). After the sequence header comes the parameter information, followed by the function designation character. For example, the character sequence ESC[2] represents a complete console device driver output command sequence requesting the driver to erase all of the screen and position the cursor at the home position. To recognize this sequence, the driver scans all of the output requests until it receives an ESC character, which may indicate the beginning of a command sequence. As long as the characters conform to the format associated with command sequences, the driver continues to buffer them (remember, characters are being received one at a time). At the point where the function designation character is encountered, the appropriate function can be performed using the specified parameter value(s). The only question is, what does the driver do with the sequence when it detects an invalid control sequence (e.g., an invalid function letter)? The driver that comes with DOS 2.0 throws the sequence away. I've written a driver with an option that allows the user to specify that invalid sequences should be displayed on the console. This is only one of the many changes that I have completed in my rewrite of the console device driver. If you are interested in reading an interesting article on the origin of the escape sequences used by ANSI.SYS, see page 365 of the April 1984 issue of Byte magazine.